Deprecate old build system and add rust build script as target
authorPierre Krieger <pierre.krieger1708@gmail.com>
Wed, 22 Oct 2014 18:32:40 +0000 (20:32 +0200)
committerAlex Crichton <alex@alexcrichton.com>
Wed, 5 Nov 2014 19:37:34 +0000 (11:37 -0800)
src/cargo/core/manifest.rs
src/cargo/ops/cargo_rustc/context.rs
src/cargo/ops/cargo_rustc/mod.rs
src/cargo/util/toml.rs
tests/test_cargo_compile_custom_build.rs [new file with mode: 0644]
tests/test_cargo_compile_old_custom_build.rs
tests/tests.rs

index bd95992796c6e20738f589be58e97f5121d0ff4c..666790746152e7597241d8c36db45c23f779df3c 100644 (file)
@@ -16,7 +16,7 @@ pub struct Manifest {
     targets: Vec<Target>,
     target_dir: Path,
     doc_dir: Path,
-    build: Vec<String>,
+    build: Vec<String>,         // TODO: deprecated, remove
     warnings: Vec<String>,
     exclude: Vec<String>,
     metadata: ManifestMetadata,
@@ -59,7 +59,7 @@ pub struct SerializedManifest {
     targets: Vec<Target>,
     target_dir: String,
     doc_dir: String,
-    build: Option<Vec<String>>,
+    build: Option<Vec<String>>,     // TODO: deprecated, remove
 }
 
 impl<E, S: Encoder<E>> Encodable<S, E> for Manifest {
@@ -73,6 +73,7 @@ impl<E, S: Encoder<E>> Encodable<S, E> for Manifest {
             targets: self.targets.clone(),
             target_dir: self.target_dir.display().to_string(),
             doc_dir: self.doc_dir.display().to_string(),
+            // TODO: deprecated, remove
             build: if self.build.len() == 0 { None } else { Some(self.build.clone()) },
         }.encode(s)
     }
@@ -131,8 +132,9 @@ pub struct Profile {
     doctest: bool,
     doc: bool,
     dest: Option<String>,
-    plugin: bool,
+    for_host: bool,
     harness: bool, // whether to use the test harness (--test)
+    custom_build: bool,
 }
 
 impl Profile {
@@ -146,8 +148,9 @@ impl Profile {
             test: false,
             doc: false,
             dest: None,
-            plugin: false,
+            for_host: false,
             doctest: false,
+            custom_build: false,
             harness: true,
         }
     }
@@ -219,8 +222,13 @@ impl Profile {
         self.doctest
     }
 
-    pub fn is_plugin(&self) -> bool {
-        self.plugin
+    pub fn is_custom_build(&self) -> bool {
+        self.custom_build
+    }
+
+    /// Returns true if the target must be built for the host instead of the target.
+    pub fn is_for_host(&self) -> bool {
+        self.for_host
     }
 
     pub fn get_opt_level(&self) -> uint {
@@ -282,8 +290,9 @@ impl Profile {
         self
     }
 
-    pub fn plugin(mut self, plugin: bool) -> Profile {
-        self.plugin = plugin;
+    /// Sets whether the `Target` must be compiled for the host instead of the target platform.
+    pub fn for_host(mut self, for_host: bool) -> Profile {
+        self.for_host = for_host;
         self
     }
 
@@ -291,6 +300,12 @@ impl Profile {
         self.harness = harness;
         self
     }
+
+    /// Sets whether the `Target` is a custom build script.
+    pub fn custom_build(mut self, custom_build: bool) -> Profile {
+        self.custom_build = custom_build;
+        self
+    }
 }
 
 impl<H: hash::Writer> hash::Hash<H> for Profile {
@@ -302,7 +317,7 @@ impl<H: hash::Writer> hash::Hash<H> for Profile {
             codegen_units,
             debug,
             rpath,
-            plugin,
+            for_host,
             ref dest,
             harness,
 
@@ -313,8 +328,10 @@ impl<H: hash::Writer> hash::Hash<H> for Profile {
             env: _,
             test: _,
             doctest: _,
+            
+            custom_build: _,
         } = *self;
-        (opt_level, codegen_units, debug, rpath, plugin, dest, harness).hash(into)
+        (opt_level, codegen_units, debug, rpath, for_host, dest, harness).hash(into)
     }
 }
 
@@ -373,7 +390,7 @@ impl Manifest {
             targets: targets,
             target_dir: target_dir,
             doc_dir: doc_dir,
-            build: build,
+            build: build,     // TODO: deprecated, remove
             warnings: Vec::new(),
             exclude: exclude,
             metadata: metadata,
@@ -466,6 +483,18 @@ impl Target {
         }
     }
 
+    /// Builds a `Target` corresponding to the `build = "build.rs"` entry.
+    pub fn custom_build_target(name: &str, src_path: &Path, profile: &Profile,
+                               metadata: Option<Metadata>) -> Target {
+        Target {
+            kind: BinTarget,
+            name: name.to_string(),
+            src_path: src_path.clone(),
+            profile: profile.clone(),
+            metadata: metadata,
+        }
+    }
+
     pub fn example_target(name: &str, src_path: &Path, profile: &Profile) -> Target {
         Target {
             kind: ExampleTarget,
index 0ea5dc8f8fcb0b2021769e954f45b8041a5732d1..7192aab7d7150fdd287176760a64e72d609faeae 100644 (file)
@@ -145,7 +145,7 @@ impl<'a, 'b: 'a> Context<'a, 'b> {
         if !visiting.insert(pkg.get_package_id()) { return }
 
         let key = (pkg.get_package_id(), target.get_name());
-        let req = if target.get_profile().is_plugin() {PlatformPlugin} else {req};
+        let req = if target.get_profile().is_for_host() {PlatformPlugin} else {req};
         match self.requirements.entry(key) {
             Occupied(mut entry) => { *entry.get_mut() = entry.get().combine(req); }
             Vacant(entry) => { entry.set(req); }
@@ -207,7 +207,7 @@ impl<'a, 'b: 'a> Context<'a, 'b> {
             ret.push(format!("{}{}", stem, self.target_exe));
         } else {
             if target.is_dylib() {
-                let plugin = target.get_profile().is_plugin();
+                let plugin = target.get_profile().is_for_host();
                 let kind = if plugin {KindPlugin} else {KindTarget};
                 let (prefix, suffix) = try!(self.dylib(kind));
                 ret.push(format!("{}{}{}", prefix, stem, suffix));
index 146b9d861b09aa0b1740ec231e8a4f79ee462b6f..51d0644cffb15c836351efff0cfe65345f0c4c2d 100644 (file)
@@ -153,11 +153,11 @@ fn compile<'a, 'b>(targets: &[&'a Target], pkg: &'a Package,
     }
     jobs.enqueue(pkg, jq::StageStart, init);
 
-    // First part of the build step of a target is to execute all of the custom
-    // build commands.
+    // Old custom build system
+    // TODO: deprecated, remove
     let mut build_cmds = Vec::new();
     for (i, build_cmd) in pkg.get_manifest().get_build().iter().enumerate() {
-        let work = try!(compile_custom(pkg, build_cmd.as_slice(), cx, i == 0));
+        let work = try!(compile_custom_old(pkg, build_cmd.as_slice(), cx, i == 0));
         build_cmds.push(work);
     }
     let (freshness, dirty, fresh) =
@@ -179,7 +179,8 @@ fn compile<'a, 'b>(targets: &[&'a Target], pkg: &'a Package,
     //
     // Each target has its own concept of freshness to ensure incremental
     // rebuilds on the *target* granularity, not the *package* granularity.
-    let (mut libs, mut bins, mut tests) = (Vec::new(), Vec::new(), Vec::new());
+    let (mut builds, mut libs, mut bins, mut tests) = (Vec::new(), Vec::new(),
+                                                       Vec::new(), Vec::new());
     for &target in targets.iter() {
         let work = if target.get_profile().is_doc() {
             let (rustdoc, desc) = try!(rustdoc(pkg, target, cx));
@@ -189,11 +190,14 @@ fn compile<'a, 'b>(targets: &[&'a Target], pkg: &'a Package,
             try!(rustc(pkg, target, cx, req))
         };
 
-        let dst = match (target.is_lib(), target.get_profile().is_test()) {
-            (_, true) => &mut tests,
-            (true, _) => &mut libs,
-            (false, false) if target.get_profile().get_env() == "test" => &mut tests,
-            (false, false) => &mut bins,
+        let dst = match (target.is_lib(),
+                         target.get_profile().is_test(),
+                         target.get_profile().is_custom_build()) {
+            (_, _, true) => &mut builds,
+            (_, true, _) => &mut tests,
+            (true, _, _) => &mut libs,
+            (false, false, _) if target.get_profile().get_env() == "test" => &mut tests,
+            (false, false, _) => &mut bins,
         };
         for (work, kind, desc) in work.into_iter() {
             let (freshness, dirty, fresh) =
@@ -203,14 +207,16 @@ fn compile<'a, 'b>(targets: &[&'a Target], pkg: &'a Package,
             dst.push((job(dirty, fresh, desc), freshness));
         }
     }
+    jobs.enqueue(pkg, jq::StageCustomBuild, builds);
     jobs.enqueue(pkg, jq::StageLibraries, libs);
     jobs.enqueue(pkg, jq::StageBinaries, bins);
     jobs.enqueue(pkg, jq::StageTests, tests);
     Ok(())
 }
 
-fn compile_custom(pkg: &Package, cmd: &str,
-                  cx: &Context, first: bool) -> CargoResult<Work> {
+// TODO: deprecated, remove
+fn compile_custom_old(pkg: &Package, cmd: &str,
+                      cx: &Context, first: bool) -> CargoResult<Work> {
     let root = cx.get_package(cx.resolve.root());
     let profile = root.get_manifest().get_targets().iter()
                       .find(|target| target.get_profile().get_env() == cx.env())
@@ -533,7 +539,7 @@ fn build_deps_args(mut cmd: ProcessBuilder, target: &Target, package: &Package,
         // target directory (hence the || here).
         let layout = cx.layout(pkg, match kind {
             KindPlugin => KindPlugin,
-            KindTarget if target.get_profile().is_plugin() => KindPlugin,
+            KindTarget if target.get_profile().is_for_host() => KindPlugin,
             KindTarget => KindTarget,
         });
 
@@ -562,7 +568,7 @@ pub fn process<T: ToCStr>(cmd: T, pkg: &Package,
     // or their dependencies
     let mut native_search_paths = HashSet::new();
     for &(dep, target) in cx.dep_targets(pkg).iter() {
-        if !target.get_profile().is_plugin() { continue }
+        if !target.get_profile().is_for_host() { continue }
         each_dep(dep, cx, |dep| {
             if dep.get_manifest().get_build().len() > 0 {
                 native_search_paths.insert(layout.native(dep));
index f92d498ce80efa0fb4de91fbbc64b3f606d9288f..01d3beb5d0deb60ccbc4142cc47459837a95c3bc 100644 (file)
@@ -251,7 +251,7 @@ pub struct TomlProject {
     name: String,
     version: TomlVersion,
     authors: Vec<String>,
-    build: Option<TomlBuildCommandsList>,
+    build: Option<TomlBuildCommandsList>,       // TODO: `String` instead
     exclude: Option<Vec<String>>,
 
     // package metadata
@@ -264,6 +264,7 @@ pub struct TomlProject {
     repository: Option<String>,
 }
 
+// TODO: deprecated, remove
 #[deriving(Decodable)]
 pub enum TomlBuildCommandsList {
     SingleBuildCommand(String),
@@ -440,10 +441,24 @@ impl TomlManifest {
             self.bench.as_ref().unwrap().iter().map(|t| t.clone()).collect()
         };
 
+        // processing the custom build script
+        let (new_build, old_build) = match project.build {
+            Some(SingleBuildCommand(ref cmd)) => {
+                if cmd.as_slice().ends_with(".rs") && layout.root.join(cmd.as_slice()).exists() {
+                    (Some(Path::new(cmd.as_slice())), Vec::new())
+                } else {
+                    (None, vec!(cmd.clone()))
+                }
+            },
+            Some(MultipleBuildCommands(ref cmd)) => (None, cmd.clone()),
+            None => (None, Vec::new())
+        };
+
         // Get targets
         let profiles = self.profile.clone().unwrap_or(Default::default());
         let targets = normalize(lib.as_slice(),
                                 bins.as_slice(),
+                                new_build,
                                 examples.as_slice(),
                                 tests.as_slice(),
                                 benches.as_slice(),
@@ -476,13 +491,10 @@ impl TomlManifest {
             }
         }
 
-        let build = match project.build {
-            Some(SingleBuildCommand(ref cmd)) => vec!(cmd.clone()),
-            Some(MultipleBuildCommands(ref cmd)) => cmd.clone(),
-            None => Vec::new()
-        };
         let exclude = project.exclude.clone().unwrap_or(Vec::new());
 
+        let has_old_build = old_build.len() >= 1;
+
         let summary = try!(Summary::new(pkgid, deps,
                                         self.features.clone()
                                             .unwrap_or(HashMap::new())));
@@ -500,13 +512,16 @@ impl TomlManifest {
                                          targets,
                                          layout.root.join("target"),
                                          layout.root.join("doc"),
-                                         build,
+                                         old_build,
                                          exclude,
                                          metadata);
         if used_deprecated_lib {
             manifest.add_warning(format!("the [[lib]] section has been \
                                           deprecated in favor of [lib]"));
         }
+        if has_old_build {
+            manifest.add_warning(format!("warning: the old build command has been deprecated"));
+        }
         Ok((manifest, nested_paths))
     }
 }
@@ -623,6 +638,7 @@ impl fmt::Show for TomlPathValue {
 
 fn normalize(libs: &[TomlLibTarget],
              bins: &[TomlBinTarget],
+             custom_build: Option<Path>,
              examples: &[TomlExampleTarget],
              tests: &[TomlTestTarget],
              benches: &[TomlBenchTarget],
@@ -689,7 +705,7 @@ fn normalize(libs: &[TomlLibTarget],
         }
 
         if target.plugin == Some(true) {
-            ret = ret.into_iter().map(|p| p.plugin(true)).collect();
+            ret = ret.into_iter().map(|p| p.for_host(true)).collect();
         }
 
         ret
@@ -747,6 +763,21 @@ fn normalize(libs: &[TomlLibTarget],
         }
     }
 
+    fn custom_build_target(dst: &mut Vec<Target>, cmd: &Path,
+                           profiles: &TomlProfiles) {
+        let profiles = [
+            merge(Profile::default_dev().for_host(true), &profiles.dev),
+            merge(Profile::default_release().for_host(true), &profiles.release),
+        ];
+
+        let name = format!("build-script-{}", cmd.filestem_str().unwrap_or(""));
+
+        for profile in profiles.iter() {
+            dst.push(Target::custom_build_target(name.as_slice(),
+                                                 cmd, profile, None));
+        }
+    }
+
     fn example_targets(dst: &mut Vec<Target>, examples: &[TomlExampleTarget],
                        profiles: &TomlProfiles,
                        default: |&TomlExampleTarget| -> String) {
@@ -829,6 +860,9 @@ fn normalize(libs: &[TomlLibTarget],
         ([], []) => ()
     }
 
+    if let Some(custom_build) = custom_build {
+        custom_build_target(&mut ret, &custom_build, profiles);
+    }
 
     example_targets(&mut ret, examples, profiles,
                     |ex| format!("examples/{}.rs", ex.name));
diff --git a/tests/test_cargo_compile_custom_build.rs b/tests/test_cargo_compile_custom_build.rs
new file mode 100644 (file)
index 0000000..0853d9d
--- /dev/null
@@ -0,0 +1,25 @@
+use support::{project, execs};
+use hamcrest::{assert_that};
+
+fn setup() {
+}
+
+test!(custom_build_compiled {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+
+            name = "foo"
+            version = "0.5.0"
+            authors = ["wycats@example.com"]
+            build = 'build.rs'
+        "#)
+        .file("src/main.rs", r#"
+            fn main() {}
+        "#)
+        .file("build.rs", r#"
+               invalid rust file, should trigger a build error
+        "#);
+    assert_that(p.cargo_process("build"),
+                execs().with_status(101));
+})
index e48986fc601d5ef4d9a15e1c3565e13d29a86206..321dd8167081e1ea1e3aebe95558f91431b8179a 100644 (file)
@@ -44,7 +44,7 @@ test!(old_custom_build {
                 execs().with_status(0)
                        .with_stdout(format!("   Compiling foo v0.5.0 ({})\n",
                                             p.url()))
-                       .with_stderr(""));
+                       .with_stderr("warning: the old build command has been deprecated"));
 })
 
 test!(old_custom_multiple_build {
@@ -108,7 +108,7 @@ test!(old_custom_multiple_build {
                 execs().with_status(0)
                        .with_stdout(format!("   Compiling foo v0.5.0 ({})\n",
                                             p.url()))
-                       .with_stderr(""));
+                       .with_stderr("warning: the old build command has been deprecated"));
 })
 
 test!(old_custom_build_failure {
@@ -148,6 +148,7 @@ test!(old_custom_build_failure {
         "#);
     assert_that(p.cargo_process("build"),
                 execs().with_status(101).with_stderr(format!("\
+warning: the old build command has been deprecated\n\
 Failed to run custom build command for `foo v0.5.0 ({dir})
 Process didn't exit successfully: `{}` (status=101)\n\
 --- stderr\n\
@@ -211,6 +212,7 @@ test!(old_custom_second_build_failure {
         "#);
     assert_that(p.cargo_process("build"),
                 execs().with_status(101).with_stderr(format!("\
+warning: the old build command has been deprecated\n\
 Failed to run custom build command for `foo v0.5.0 ({dir})
 Process didn't exit successfully: `{}` (status=101)\n\
 --- stderr\n\
index 49ed62565d5e637f25dafb01d81f0348e974f7ff..0c0a47851cf5748099950d8366b6a85608a1b444 100644 (file)
@@ -29,6 +29,7 @@ mod test_cargo;
 mod test_cargo_bench;
 mod test_cargo_clean;
 mod test_cargo_compile;
+mod test_cargo_compile_custom_build;
 mod test_cargo_compile_old_custom_build;
 mod test_cargo_compile_git_deps;
 mod test_cargo_compile_path_deps;